package com.gitee.ed.arithmetic.leetcode;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.gitee.ed.arithmetic.leetcode.common.ConsolePrinter;
import com.gitee.ed.arithmetic.leetcode.common.Question;
import com.gitee.ed.arithmetic.leetcode.common.Solution;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;

/**
 * @author hongda.li
 */
public final class SolutionRunner {
    /**
     * 包名称
     */
    private static final String PACKAGE_NAME = "com.gitee.ed.arithmetic.leetcode";
    /**
     * 答案的类名称前缀
     */
    private static final String ANSWER_NAME = "Answer";

    public static void main(String[] args) {
        run(11);
    }

    @SuppressWarnings("unchecked")
    public static void run(int index) {
        Set<Class<?>> scanned = ClassUtil.scanPackage(PACKAGE_NAME, clz -> {
            String simpleName = clz.getSimpleName();
            return CharSequenceUtil.equalsAny(simpleName,
                    Question.class.getSimpleName() + index,
                    ANSWER_NAME + index);
        });
        Question<Solution> question = scanned.stream()
                .filter(Question.class::isAssignableFrom)
                .findFirst()
                .map(ReflectUtil::newInstanceIfPossible)
                .map(Question.class::cast)
                .orElseThrow(NoSuchElementException::new);
        Solution solution = scanned.stream()
                .filter(Solution.class::isAssignableFrom)
                .findFirst()
                .map(ReflectUtil::newInstanceIfPossible)
                .map(Solution.class::cast)
                .orElseThrow(NoSuchElementException::new);
        run(question, solution);
    }

    /**
     * 执行用例
     *
     * @param question 问题实例
     * @param solution 算法实例
     * @param <S>      算法类型
     */
    public static <S extends Solution> void run(Question<S> question, S solution) {
        question.test(solution);
    }

    /**
     * 通过字符串来判断预期值和实际值是否相等
     *
     * @param expect 预期值
     * @param actual 实际值
     */
    public static void checkString(Object expect, Object actual) {
        String expectString = ObjectUtil.toString(expect);
        String actualString = ObjectUtil.toString(actual);
        check(() -> Objects.equals(expectString, actualString),
                () -> expectString,
                () -> actualString);
    }

    /**
     * 校验预期值和实际值是否相等
     *
     * @param equals 断言判断
     * @param expect 预期值
     * @param actual 实际值
     */
    public static void check(BooleanSupplier equals,
                             Supplier<String> expect,
                             Supplier<String> actual) {
        ConsolePrinter.printWhite("测试用例---------------------------------------");
        ConsolePrinter.printCyan("预期结果 : " + ObjectUtil.toString(expect.get()));
        ConsolePrinter.printCyan("实际结果 : " + ObjectUtil.toString(actual.get()));
        if (equals.getAsBoolean()) {
            ConsolePrinter.printGreen("通过测试用例");
        } else {
            ConsolePrinter.printRed("未通过测试用例");
            System.exit(-1);
        }
        ConsolePrinter.nextLine();
    }
}
